package com.myride;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import javax.servlet.http.HttpServlet;
import org.apache.log4j.Logger;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import db.DBUtils;
import db.NamedParameterStatement;

public class MyRide extends HttpServlet {
	static final long serialVersionUID = 1;
	static final String DB_PARAM_PREFIX = "db_";
	public static Logger logger;
	
	String log4jPropsFullPath;
	String propsFullPath;
	String logFileFullPath;
	int expansionThreshold;
	double defaultRadius;
	double maxRadius;
	String defaultTimeframe;
		
	public void init() throws javax.servlet.ServletException
	{
		try	
		{											
			log4jPropsFullPath = getServletContext().getRealPath(Utils.DEFAULT_LOG4J_FILE);
			propsFullPath = getServletContext().getRealPath(Utils.DEFAULT_PROPERTIES_FILE);
			logFileFullPath = getServletContext().getRealPath(Utils.DEFAULT_LOG_FILE);			
			
			Utils.configureLog4j(log4jPropsFullPath, "");
			logger = Logger.getLogger("myride"); 
			logger.debug("myRide Starting...");
			Utils.readProperties(propsFullPath);	
			  
			expansionThreshold = Integer.parseInt(Utils.getProperty("expansion.threshold"));
			defaultRadius = Double.parseDouble(Utils.getProperty("default.radius"));
			defaultTimeframe = Utils.getProperty("default.timeframe");			
			maxRadius = Double.parseDouble(Utils.getProperty("max.radius"));
			
			super.init();		
		}
		catch (Exception e)
		{
			e.printStackTrace();
		}		
	}
	
	public void doGet(javax.servlet.http.HttpServletRequest request,
			javax.servlet.http.HttpServletResponse response)
			throws javax.servlet.ServletException, java.io.IOException {

		performTask(request, response);

	}

	public void doPost(javax.servlet.http.HttpServletRequest request,
			javax.servlet.http.HttpServletResponse response)
			throws javax.servlet.ServletException, java.io.IOException {
		performTask(request, response);

	}				
	
	// Enumerates the request and adds parameters to the sql statement
	void collectDbParams(JSONObject clientRequest, NamedParameterStatement pstmt) throws SQLException, JSONException {		 
		JSONObject params = clientRequest.getJSONObject("params");
		String[] paramNames = JSONObject.getNames(params);
		
		if (paramNames == null) return;
		
		for (String paramName: paramNames) {			
			if (paramName.startsWith(DB_PARAM_PREFIX)) {
			    String paramValue = params.getString(paramName);
			    			    
			    if ("".equals(paramValue)) {
			        pstmt.setNull(paramName.substring(DB_PARAM_PREFIX.length()));
			    }
			    else {
			        pstmt.setObject(paramName.substring(DB_PARAM_PREFIX.length()), paramValue);
			    }
			}
		}
	}
	
	// Executes a stored procedure from the DB and returns the result set as JSON
	JSONObject execDbQuery(JSONObject clientRequest) throws IOException, javax.servlet.ServletException, SQLException, JSONException {						
		String sp = clientRequest.getString("sp");
		String cmd = "CALL " + sp + "(" + DBUtils.getSpParams(sp) + ")";				
		
		Connection conn = DBUtils.getDBConnection(); 		
		NamedParameterStatement pstmt = new NamedParameterStatement(conn, cmd);		
		
		collectDbParams(clientRequest, pstmt);				
		
		// Execute the statement		
		ResultSet rs = pstmt.executeQuery();				
		JSONObject json = DBUtils.toJSON(rs);
				
		conn.close();
		
		return json;
	}
	
	// Searches for rides
	JSONObject searchRides(JSONObject clientRequest) throws IOException, javax.servlet.ServletException, SQLException, JSONException {						
		clientRequest.put("sp", "sp_SearchRides");
		
		double radius = defaultRadius;
		JSONObject params = clientRequest.getJSONObject("params");
		params.put("db_radius", radius);
		params.put("db_timeframe", defaultTimeframe);		
		
		JSONObject response = execDbQuery(clientRequest);
		JSONArray rows = response.getJSONArray("rows");		
		while (rows.length() < expansionThreshold && radius <= maxRadius) {
			params.put("db_radius", radius);
			response = execDbQuery(clientRequest);
			rows = response.getJSONArray("rows");
			radius *= 2;
		}
				
		return response;
	}
	
	protected JSONObject sendMail(JSONObject clientRequest) throws JSONException {
		JSONObject response = new JSONObject();
		String[] recipients = clientRequest.getString("to").split(";");
		String subject = clientRequest.getString("subject");
		String message = clientRequest.getString("message");
		
		
		try {
			Utils.postMail(recipients, subject, message);
			response.put("status", "success");
		} catch (Exception e) {
			logger.error("Exception caught", e);
			response.put("status", "failed");
		}
		
		return response;
	}
	
	public void performTask(javax.servlet.http.HttpServletRequest request,
			javax.servlet.http.HttpServletResponse response) throws IOException, javax.servlet.ServletException {						
			
		response.setCharacterEncoding("UTF-8");
		request.setCharacterEncoding("UTF-8");								
		
		String requestBody = new BufferedReader(new InputStreamReader(new Base64.InputStream(request.getInputStream()), "UTF-8")).readLine();		
		//String requestBody = request.getParameter("json");
		
		System.out.println(requestBody);
		
		try {			
			JSONArray requests = new JSONArray(requestBody);									
			
			JSONArray responses = new JSONArray();
			
			for (int i = 0; i < requests.length(); i++) {				
				JSONObject clientRequest = requests.getJSONObject(i);				
								
				String op = clientRequest.getString("op");
				if ("DB".equals(op)) responses.put(execDbQuery(clientRequest));								
				if ("SEARCH".equals(op)) responses.put(searchRides(clientRequest));
				if ("SENDMAIL".equals(op)) responses.put(sendMail(clientRequest));
			}
						
			response.getWriter().print(responses.toString(2));
			
		} catch (Exception e) {
			logger.error("Exception caught", e);
			e.printStackTrace();
			response.sendError(500);
		}				
	}	
}
